package com.sky.utils;

import com.sky.properties.WeChatProperties;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.crypto.Cipher;
import java.util.Base64;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.security.cert.CertificateFactory;

/**
 * 微信支付工具类（增强安全版）
 * 增加安全机制：私钥加密存储、证书动态更新、连接池管理、完善异常处理
 */
@Component
public class WeChatPayUtil {
    private static final Logger log = LoggerFactory.getLogger(WeChatPayUtil.class);

    // 微信支付接口地址
    public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    public static final String CERTIFICATES = "https://api.mch.weixin.qq.com/v3/certificates";

    // 证书更新间隔（23小时，避免证书过期）
    private static final long CERT_REFRESH_INTERVAL = 23 * 60 * 60 * 1000L;

    @Autowired
    private WeChatProperties weChatProperties;

    // 缓存的平台证书
    private volatile List<X509Certificate> wechatPayCertificates;

    // HTTP客户端连接池
    private CloseableHttpClient httpClient;

    // 定时任务线程池（用于更新证书）
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread thread = new Thread(r, "wechat-pay-cert-refresh");
        thread.setDaemon(true);
        return thread;
    });

    /**
     * 初始化方法，启动时加载证书并定时更新
     */
    public void init() {
        try {
            // 初始化加载证书
            refreshCertificates();

            // 初始化HTTP客户端
            initHttpClient();

            // 启动定时更新证书任务
            scheduler.scheduleAtFixedRate(this::refreshCertificates,
                    CERT_REFRESH_INTERVAL, CERT_REFRESH_INTERVAL, TimeUnit.MILLISECONDS);

            log.info("微信支付工具类初始化完成，证书定时更新任务已启动");
        } catch (Exception e) {
            log.error("微信支付工具类初始化失败", e);
            throw new RuntimeException("微信支付工具类初始化失败", e);
        }
    }

    /**
     * 初始化HTTP客户端连接池
     */
    private void initHttpClient() throws Exception {
        PrivateKey merchantPrivateKey = getDecryptedPrivateKey();
        Assert.notNull(merchantPrivateKey, "商户私钥加载失败");
        Assert.notEmpty(wechatPayCertificates, "平台证书列表为空");

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
                .withWechatPay(wechatPayCertificates);

        // 配置连接池
        builder.setMaxConnTotal(50)
                .setMaxConnPerRoute(20);

        httpClient = builder.build();
    }

    /**
     * 刷新平台证书
     */
    private synchronized void refreshCertificates() {
        log.info("开始刷新微信支付平台证书");
        try (CloseableHttpClient tempClient = createTempHttpClient()) {
            HttpGet httpGet = new HttpGet(CERTIFICATES);
            httpGet.addHeader(HttpHeaders.ACCEPT, "application/json");
            httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());

            try (CloseableHttpResponse response = tempClient.execute(httpGet)) {
                String body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                log.info("获取平台证书响应: {}", body);

                JSONObject json = JSONObject.parseObject(body);
                List<JSONObject> certificates = json.getJSONArray("data").toJavaList(JSONObject.class);

                // 解密并解析证书
                List<X509Certificate> newCertificates = certificates.stream()
                        .map(this::decryptCertificate)
                        .collect(Collectors.toList());

                // 验证证书有效性
                validateCertificates(newCertificates);

                // 原子更新证书
                wechatPayCertificates = newCertificates;
                log.info("平台证书刷新成功，共加载 {} 个证书", newCertificates.size());

                // 重新初始化HTTP客户端
                initHttpClient();
            }
        } catch (Exception e) {
            log.error("平台证书刷新失败", e);
            // 保留旧证书，避免服务中断
            if (wechatPayCertificates == null || wechatPayCertificates.isEmpty()) {
                log.error("无可用证书，微信支付功能将不可用");
            } else {
                log.warn("使用旧证书继续运行，建议尽快排查证书更新问题");
            }
        }
    }

    /**
     * 创建临时HTTP客户端（用于获取证书）
     */
    private CloseableHttpClient createTempHttpClient() throws Exception {
        PrivateKey merchantPrivateKey = getDecryptedPrivateKey();
        return WechatPayHttpClientBuilder.create()
                .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
                .build();
    }

    /**
     * 解密证书
     */
    private X509Certificate decryptCertificate(JSONObject certObj) {
        try {
            String encryptedCert = certObj.getString("encrypt_certificate");
            String associatedData = JSONObject.parseObject(encryptedCert).getString("associated_data");
            String nonce = JSONObject.parseObject(encryptedCert).getString("nonce");
            String cipherText = JSONObject.parseObject(encryptedCert).getString("ciphertext");

            // 使用APIv3密钥解密证书
            byte[] decrypted = decryptAesGcm(
                    weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8),
                    Base64.getDecoder().decode(cipherText),
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8)
            );

            // 解析证书
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(decrypted));
        } catch (Exception e) {
            log.error("证书解密失败", e);
            throw new RuntimeException("证书解密失败", e);
        }
    }

    /**
     * AES-GCM解密
     */
    private byte[] decryptAesGcm(byte[] key, byte[] cipherText, byte[] associatedData, byte[] nonce) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        javax.crypto.spec.GCMParameterSpec parameterSpec = new javax.crypto.spec.GCMParameterSpec(128, nonce);
        javax.crypto.spec.SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, parameterSpec);
        cipher.updateAAD(associatedData);
        return cipher.doFinal(cipherText);
    }

    /**
     * 验证证书有效性
     */
    private void validateCertificates(List<X509Certificate> certificates) {
        Instant now = Instant.now();
        for (X509Certificate cert : certificates) {
            try {
                cert.checkValidity(Date.from(now));
            } catch (Exception e) {
                log.error("证书无效或已过期: {}", cert.getSerialNumber(), e);
                throw new RuntimeException("证书无效或已过期", e);
            }
        }
    }

    /**
     * 获取解密后的私钥（从加密存储中解密）
     */
    private PrivateKey getDecryptedPrivateKey() throws Exception {
        // 从安全存储获取加密的私钥（示例：从文件读取加密私钥）
        String encryptedPrivateKey = readEncryptedPrivateKeyFromFile();

        // 使用密钥管理服务解密私钥（实际应用中应使用KMS等服务）
        String decryptedPrivateKey = decryptPrivateKey(encryptedPrivateKey);

        // 加载私钥
        return PemUtil.loadPrivateKey(new ByteArrayInputStream(decryptedPrivateKey.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 从文件读取加密的私钥
     */
    private String readEncryptedPrivateKeyFromFile() throws IOException {
        File privateKeyFile = new File(weChatProperties.getPrivateKeyFilePath());
        try (FileInputStream fis = new FileInputStream(privateKeyFile)) {
            byte[] data = new byte[(int) privateKeyFile.length()];
            fis.read(data);
            return new String(data, StandardCharsets.UTF_8);
        }
    }

    /**
     * 解密私钥（实际应用中应使用KMS等服务解密）
     */
    private String decryptPrivateKey(String encryptedPrivateKey) {
        // 这里仅为示例，实际应用中应使用安全的解密方式
        // 例如：调用阿里云KMS、AWS KMS等服务解密
        try {
            // 示例：使用APIv3密钥进行简单解密（实际需替换为安全解密逻辑）
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] key = weChatProperties.getApiV3Key().substring(0, 16).getBytes(StandardCharsets.UTF_8);
            byte[] nonce = new byte[12];
            new SecureRandom().nextBytes(nonce);
            javax.crypto.spec.GCMParameterSpec parameterSpec = new javax.crypto.spec.GCMParameterSpec(128, nonce);
            javax.crypto.spec.SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(key, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, parameterSpec);
            return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedPrivateKey)), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("私钥解密失败", e);
            throw new RuntimeException("私钥解密失败", e);
        }
    }

    /**
     * 发送post方式请求
     */
    private String post(String url, String body) throws Exception {
        Assert.notNull(httpClient, "HTTP客户端未初始化");

        HttpPost httpPost = new HttpPost(url);
        addCommonHeaders(httpPost);
        httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            int statusCode = response.getStatusLine().getStatusCode();
            String bodyAsString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

            log.info("POST请求 [{}] 响应: 状态码={}, 响应体={}", url, statusCode, bodyAsString);

            if (statusCode < 200 || statusCode >= 300) {
                throw new WeChatPayException("微信支付接口调用失败: " + bodyAsString, statusCode);
            }
            return bodyAsString;
        } catch (Exception e) {
            log.error("POST请求 [{}] 失败, 请求体: {}", url, body, e);
            throw new WeChatPayException("微信支付接口调用异常", e);
        }
    }

    /**
     * 发送get方式请求
     */
    private String get(String url) throws Exception {
        Assert.notNull(httpClient, "HTTP客户端未初始化");

        HttpGet httpGet = new HttpGet(url);
        addCommonHeaders(httpGet);

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            int statusCode = response.getStatusLine().getStatusCode();
            String bodyAsString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

            log.info("GET请求 [{}] 响应: 状态码={}, 响应体={}", url, statusCode, bodyAsString);

            if (statusCode < 200 || statusCode >= 300) {
                throw new WeChatPayException("微信支付接口调用失败: " + bodyAsString, statusCode);
            }
            return bodyAsString;
        } catch (Exception e) {
            log.error("GET请求 [{}] 失败", url, e);
            throw new WeChatPayException("微信支付接口调用异常", e);
        }
    }

    /**
     * 添加公共请求头
     */
    private void addCommonHeaders(HttpPost httpPost) {
        httpPost.addHeader(HttpHeaders.ACCEPT, "application/json");
        httpPost.addHeader(HttpHeaders.CONTENT_TYPE, "application/json");
        httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
        httpPost.addHeader("Wechatpay-Timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        httpPost.addHeader("Wechatpay-Nonce", RandomStringUtils.randomAlphanumeric(32));
    }

    /**
     * 添加公共请求头
     */
    private void addCommonHeaders(HttpGet httpGet) {
        httpGet.addHeader(HttpHeaders.ACCEPT, "application/json");
        httpGet.addHeader(HttpHeaders.CONTENT_TYPE, "application/json");
        httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
        httpGet.addHeader("Wechatpay-Timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        httpGet.addHeader("Wechatpay-Nonce", RandomStringUtils.randomAlphanumeric(32));
    }

    /**
     * jsapi下单
     */
    private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appid", weChatProperties.getAppid());
        jsonObject.put("mchid", weChatProperties.getMchid());
        jsonObject.put("description", description);
        jsonObject.put("out_trade_no", orderNum);
        jsonObject.put("notify_url", weChatProperties.getNotifyUrl());

        JSONObject amount = new JSONObject();
        // 修复金额计算精度问题
        amount.put("total", total.setScale(2, BigDecimal.ROUND_HALF_UP)
                .multiply(new BigDecimal(100)).intValue());
        amount.put("currency", "CNY");

        jsonObject.put("amount", amount);

        JSONObject payer = new JSONObject();
        payer.put("openid", openid);

        jsonObject.put("payer", payer);

        String body = jsonObject.toJSONString();
        log.info("JSAPI下单请求: {}", body);
        return post(JSAPI, body);
    }

    /**
     * 小程序支付
     */
    public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
        log.info("发起支付请求, 订单号: {}", orderNum);
        // 统一下单，生成预支付交易单
        String bodyAsString = jsapi(orderNum, total, description, openid);
        // 解析返回结果
        JSONObject jsonObject = JSONObject.parseObject(bodyAsString);
        log.info("统一下单返回: {}", jsonObject);

        String prepayId = jsonObject.getString("prepay_id");
        if (prepayId != null) {
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonceStr = RandomStringUtils.randomNumeric(32);
            ArrayList<Object> list = new ArrayList<>();
            list.add(weChatProperties.getAppid());
            list.add(timeStamp);
            list.add(nonceStr);
            list.add("prepay_id=" + prepayId);

            // 二次签名，调起支付需要重新签名
            String signMessage = list.stream().map(String::valueOf).collect(Collectors.joining("\n", "", "\n"));
            log.info("二次签名原文: {}", signMessage);

            // 使用缓存的私钥进行签名，避免重复加载
            PrivateKey privateKey = getDecryptedPrivateKey();
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(signMessage.getBytes(StandardCharsets.UTF_8));
            String packageSign = Base64.getEncoder().encodeToString(signature.sign());

            // 构造数据给微信小程序，用于调起微信支付
            JSONObject jo = new JSONObject();
            jo.put("timeStamp", timeStamp);
            jo.put("nonceStr", nonceStr);
            jo.put("package", "prepay_id=" + prepayId);
            jo.put("signType", "RSA");
            jo.put("paySign", packageSign);

            return jo;
        }
        return jsonObject;
    }

    /**
     * 申请退款
     */
    public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception {
        log.info("发起退款请求, 订单号: {}, 退款单号: {}", outTradeNo, outRefundNo);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("out_trade_no", outTradeNo);
        jsonObject.put("out_refund_no", outRefundNo);

        JSONObject amount = new JSONObject();
        // 修复金额计算精度问题
        amount.put("refund", refund.setScale(2, BigDecimal.ROUND_HALF_UP)
                .multiply(new BigDecimal(100)).intValue());
        amount.put("total", total.setScale(2, BigDecimal.ROUND_HALF_UP)
                .multiply(new BigDecimal(100)).intValue());
        amount.put("currency", "CNY");

        jsonObject.put("amount", amount);
        jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl());

        String body = jsonObject.toJSONString();
        log.info("退款请求: {}", body);

        return post(REFUNDS, body);
    }

    /**
     * 验证支付通知签名
     */
    public boolean verifyNotifySignature(String serial, String timestamp, String nonce, String body, String signature) {
        try {
            // 查找对应的证书
            X509Certificate certificate = wechatPayCertificates.stream()
                    .filter(cert -> cert.getSerialNumber().toString(16).equalsIgnoreCase(serial))
                    .findFirst()
                    .orElseThrow(() -> new WeChatPayException("未找到对应的平台证书, serial: " + serial));

            // 构造签名串
            String signMessage = String.join("\n", serial, timestamp, nonce, body) + "\n";

            // 验证签名
            Signature sig = Signature.getInstance("SHA256withRSA");
            sig.initVerify(certificate);
            sig.update(signMessage.getBytes(StandardCharsets.UTF_8));
            boolean verified = sig.verify(Base64.getDecoder().decode(signature));

            if (!verified) {
                log.warn("通知签名验证失败, 签名串: {}", signMessage);
            }
            return verified;
        } catch (Exception e) {
            log.error("通知签名验证异常", e);
            return false;
        }
    }

    /**
     * 销毁方法，释放资源
     */
    public void destroy() {
        try {
            if (httpClient != null) {
                httpClient.close();
            }
        } catch (IOException e) {
            log.error("HTTP客户端关闭失败", e);
        }
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
        }
        log.info("微信支付工具类已销毁");
    }

    /**
     * 微信支付自定义异常
     */
    public static class WeChatPayException extends Exception {
        private int errorCode;

        public WeChatPayException(String message) {
            super(message);
        }

        public WeChatPayException(String message, Throwable cause) {
            super(message, cause);
        }

        public WeChatPayException(String message, int errorCode) {
            super(message);
            this.errorCode = errorCode;
        }

        public int getErrorCode() {
            return errorCode;
        }
    }
}
